---
title: Adicione funcionalidades de i18n
description: Use roteamento dinâmico e coleções de conteúdo para adicionar suporte à internacionalização no seu site feito com Astro.
type: recipe
i18nReady: true
---
import { FileTree } from '@astrojs/starlight/components';
import StaticSsrTabs from '~/components/tabs/StaticSsrTabs.astro'

Astro não tem suporte integrado a internacionalização (i18n), mas você pode construir a sua própria solução.
Nesta receita, você vai aprender como usar as coleções de conteúdo e roteamento dinâmico para fornecer conteúdo em diferentes línguas.

Esse exemplo utiliza cada língua como seu próprio subcaminho, e.x. `example.com/en/blog` para Inglês e `example.com/fr/blog` para Francês.

Se você prefere que a língua padrão não seja visível na URL ao contrário de outras línguas, abaixo tem [instruções para ocultar a língua padrão](/pt-br/recipes/i18n/#ocultar-a-língua-padrão-na-url).

## Receita

### Configure páginas para cada língua

1. Crie um diretório para cada língua que você quer oferecer suporte. Por exemplo, `en/` e `fr/` se você estiver oferecendo suporte para inglês e francês:

    <FileTree>
    - src/
      - pages/
        - **en/**
          - about.astro
          - index.astro
        - **fr/**
          - about.astro
          - index.astro
        - index.astro
    </FileTree>

    
2. Configure `src/pages/index.astro` para redirecionar para a língua padrão.

    <StaticSsrTabs>
      <Fragment slot="static">
        ```astro
        ---
        // src/pages/index.astro
        ---
        <meta http-equiv="refresh" content="0;url=/en/" />
        ```

        Esta abordagem utiliza [meta refresh](https://en.wikipedia.org/wiki/Meta_refresh) e funcionará independentemente de como você fizer deploy do seu site. No entanto, alguns hosts estáticos também permitem que você configure redirecionamentos pelo servidor com um arquivo de configuração personalizado. Verifique a documentação da sua plataforma de deploy para mais detalhes.
      </Fragment>
      
      <Fragment slot="ssr">
        Se você está utilizando um adaptador SSR, você pode usar [`Astro.redirect`](/pt-br/guides/routing/#redirecionamentos-dinâmicos) para redirecionar para a linguagem padrão no servidor.

        ```astro
        ---
        // src/pages/index.astro
        return Astro.redirect('/en/');
        ---
        ```
      </Fragment>
    </StaticSsrTabs>

### Use coleções para conteúdo traduzido

1. Crie um diretório em `src/content/` para cada tipo de conteúdo que você quer incluir e adicione subdiretórios para cada língua suportada. Por exemplo, para oferecer suporte a postagens de blog em inglês e francês:

    <FileTree>
    - src/
      - content/
          - blog/
            - **en/** Postagens de blog em inglês
                - post-1.md
                - post-2.md
            - **fr/** Postagens de blog em francês
              - post-1.md
              - post-2.md
    </FileTree>

2. Crie um arquivo `src/content/config.ts` e exporte uma coleção para cada tipo de conteúdo.

    ```ts
    //src/content/config.ts
    import { defineCollection, z } from 'astro:content';

    const blogCollection = defineCollection({
      schema: z.object({
        title: z.string(),
        author: z.string(),
        date: z.date()
      })
    });

    export const collections = {
      'blog': blogCollection
    };

    ```
    📚 Leia mais sobre [Coleções de Conteúdo](/pt-br/guides/content-collections/).

3. Use [rotas dinâmicas](/pt-br/guides/routing/#rotas-dinâmicas) para buscar e renderizar conteúdo baseado em parâmetros `lang` e `slug`.

    <StaticSsrTabs>
      <Fragment slot="static">
        Em modo de renderização estático, use `getStaticPaths` para mapear cada entrada de conteúdo para uma página:
        ```astro
        //src/pages/[lang]/blog/[...slug].astro
        ---
        import { getCollection } from 'astro:content'

        export async function getStaticPaths() {
          const paginas = await getCollection('blog')

          const caminhos = paginas.map(pagina => {
            const [lang, ...slug] = pagina.slug.split('/');
            return { params: { lang, slug: slug.join('/') || undefined }, props: pagina }
          })

          return caminhos;
        }

        const { lang, slug } = Astro.params;
        const pagina = Astro.props;
        const dataFormatada = pagina.data.data.toLocaleString(lang);

        const { Content } = await page.render();
        ---
        <h1>{page.data.titulo}</h1>
        <p>by {page.data.autor} • {dataFormatada}</p>
        <Content/>
        ```
      </Fragment>

      <Fragment slot="ssr">
        Em [modo SSR](/pt-br/guides/server-side-rendering/), busque a entrada solicitada diretamente:

        ```astro
        //src/pages/[lang]/blog/[...slug].astro
        ---
        import { getEntryBySlug } from 'astro:content';

        const { lang, slug } = Astro.params;
        const pagina = await getEntryBySlug('blog', `${lang}/${slug}`);

        if (!pagina) {
          return Astro.redirect('/404');
        }

        const dataFormatada = page.data.data.toLocaleString(lang);
        const { Content, headings } = await page.render();
        ---
        <h1>{page.data.titulo}</h1>
        <p>by {page.data.autor} • {dataFormatada}</p>
        <Content/>
        ```
      </Fragment>
    </StaticSsrTabs>

    📚 Leia mais sobre [roteamento dinâmico](/pt-br/guides/routing/#rotas-dinâmicas).

    :::tip[Formatação de data]
    O exemplo acima utiliza o método de formatação de data [`toLocaleString()`](https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleString) para criar uma string legível a partir da data da página.
    Isso garante que a data e a hora sejam formatadas de acordo com a língua do usuário.
    :::

### Traduza strings da UI

Criar dicionários de termos para traduzir as labels dos elementos de UI em todo o seu site. Isso permite que seus visitantes experimentem seu site completamente na língua deles.
1. Crie um arquivo `src/i18n/ui.ts` para armazenar suas strings de tradução:

    ```ts
    // src/i18n/ui.ts
    export const linguas = {
      en: 'English',
      fr: 'Français',
    };

    export const linguaPadrao = 'en';

    export const ui = {
      en: {
        'nav.home': 'Home',
        'nav.about': 'About',
        'nav.twitter': 'Twitter',
      },
      fr: {
        'nav.home': 'Accueil',
        'nav.about': 'À propos',
      },
    } as const;
    ```
    
2. Crie duas funções: uma para detectar a língua da página baseado na URL atual, e outra para obter as strings de tradução para diferentes partes da UI em `src/i18n/utils.ts`:

    ```js
    // src/i18n/utils.ts
    import { ui, linguaPadrao } from './ui';

    export function pegarLangDeURL(url: URL) {
      const [, lang] = url.pathname.split('/');
      if (lang in ui) return lang as keyof typeof ui;
      return linguaPadrao;
    }

    export function usarTraducoes(lang: keyof typeof ui) {
      return function t(key: keyof typeof ui[typeof linguaPadrao]) {
        return ui[lang][key] || ui[linguaPadrao][key];
      }
    }
    ```

    :::note[Você percebeu?]
    No passo 1, a string `nav.twitter` não foi traduzida para o francês. Você pode não querer traduzir todos os termos, como nomes próprios ou termos comuns da indústria. A função `useTranslations` irá retornar o valor da língua padrão se uma chave não for traduzida. Neste exemplo, os usuários franceses também verão “Twitter” na barra de navegação.
    :::
    
3. Importe as funções onde necessário e use-as para escolher a string de UI que corresponde a língua atual. Por exemplo, um componente de navegação pode ser assim: 

    ```astro 
    ---
    // src/components/Nav.astro
    import { pegarLangDeURL, usarTraducoes } from '../i18n/utils';
    
    const lang = pegarLangDeURL(Astro.url);
    const t = usarTraducoes(lang);
    ---
    <ul>
        <li>
            <a href={`/${lang}/home/`}>
              {t('nav.home')}
            </a>
        </li>
        <li>
            <a href={`/${lang}/about/`}>
              {t('nav.about')}
            </a>
        </li>
        <li>
            <a href="https://twitter.com/astrodotbuild">
              {t('nav.twitter')}
            </a>
        </li>
    </ul>
    ```

4. Cada página deve ter um atributo `lang` no elemento `<html>` que corresponda a língua da página. Neste exemplo, um [layout reutilizável](/pt-br/basics/layouts/) extrai a língua da rota atual:
    ```astro
    ---
    // src/layouts/Base.astro
    
    import { pegarLangDeURL } from '../i18n/utils';
    
    const lang = pegarLangDeURL(Astro.url);
    ---
    <html lang={lang}>
        <head>
            <meta charset="utf-8" />
            <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
            <meta name="viewport" content="width=device-width" />
            <title>Astro</title>
        </head>
        <body>
            <slot />
        </body>
    </html>
    ```

    Assim, você pode usar este layout base para garantir que as páginas usem o atributo `lang` correto automaticamente.

    ```astro
    ---
    // src/pages/en/about.astro
    import Base from "../../layouts/Base.astro"
    ---
    <Base>
        <h1>About me</h1>
        ...
    </Base>
    ```

### Permitir que os usuários alternem entre línguas

Criar links para as diferentes línguas que você oferece suporte para que os usuários possam escolher a língua que eles querem ler seu site.

1. Crie um componente para mostrar um link para cada língua:

    ```astro
    ---
    // src/components/SeletorLinguas.astro
    import { linguas } from '../i18n/ui';
    ---
    <ul>
      {Object.entries(linguas).map(([lang, label]) => (
        <li>
          <a href={`/${lang}/`}>{label}</a>
        </li>
      ))}
    </ul>
    ```

2. Adicione `<SeletorLinguas />` ao seu site para que ele seja mostrado em todas as páginas. O exemplo abaixo adiciona ele ao rodapé do site em um layout base:

    ```astro ins={3,17-19}
    ---
    // src/layouts/Base.astro
    import SeletorLinguas from '../components/SeletorLinguas.astro';
    import { pegarLangDeURL } from '../i18n/utils';
    
    const lang = pegarLangDeURL(Astro.url);
    ---
    <html lang={lang}>
        <head>
            <meta charset="utf-8" />
            <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
            <meta name="viewport" content="width=device-width" />
            <title>Astro</title>
        </head>
        <body>
            <slot />
            <footer>
              <SeletorLinguas />
            </footer>
        </body>
    </html>
    ```

### Ocultar a língua padrão na URL

1. Crie um diretório para cada língua, exceto a língua padrão. Por exemplo, armazene suas páginas da língua padrão diretamente em `pages/`, e suas páginas traduzidas em `fr/`:

    <FileTree>
    - src/
      - pages/
        - about.astro
        - index.astro
        - **fr/**
          - about.astro
          - index.astro
    </FileTree>

2. Adicione outra linha ao arquivo `src/i18n/ui.ts` para ativar o recurso:

    ```ts
    // src/i18n/ui.ts
    export const mostrarLinguaPadrao = false;
    ```


3. Adicione uma função ao arquivo `src/i18n/utils.ts`, para traduzir caminhos baseado na língua atual:
   ```js
    // src/i18n/utils.ts
    import { ui, linguaPadrao, mostrarLinguaPadrao } from './ui';

    export function usarCaminhoTraduzido(lang: keyof typeof ui) {
      return function traduzirCaminho(caminho: string, l: string = lang) {
        return !mostrarLinguaPadrao && l === linguaPadrao ? caminho : `/${l}${caminho}`
      }
    }
    ```

4. Importe a função onde for necessário. Por exemplo, um componente `nav` pode ficar assim:
    ```astro 
    ---
    // src/components/Nav.astro
    import { pegarLangDeURL, usarTraducoes, usarCaminhoTraduzido } from '../i18n/utils';
    
    const lang = pegarLangDeURL(Astro.url);
    const t = usarTraducoes(lang);
    const traduzirCaminho = usarCaminhoTraduzido(lang);
    ---
    <ul>
        <li>
            <a href={traduzirCaminho('/home/')}>
              {t('nav.home')}
            </a>
        </li>
        <li>
            <a href={traduzirCaminho('/about/')}>
              {t('nav.about')}
            </a>
        </li>
        <li>
            <a href="https://twitter.com/astrodotbuild">
              {t('nav.twitter')}
            </a>
        </li>
    </ul>
    ```

5. A função também pode ser usada para traduzir caminhos para uma língua específica. Por exemplo, quando os usuários alternam entre línguas:
    ```astro
    ---
    // src/components/SeletorLinguas.astro
    import { linguas } from '../i18n/ui';
    ---

    <ul>
      {Object.entries(linguas).map(([lang, label]) => (
        <li>
          <a href={traduzirCaminho('/', lang)}>{label}</a>
        </li>
      ))}
    </ul>
    ```

### Traduzir rotas

Traduzir as rotas das suas páginas para cada língua.

1. Adicione mapeamento de rotas no arquivo `src/i18n/ui.ts`:

 ```ts
 // src/i18n/ui.ts
 export const rotas = {
   de: {
    'services': 'leistungen',
   },
   fr: {
    'services': 'prestations-de-service',
   },
  }
    ```

2. Atualize a função `usarCaminhoTraduzido` no arquivo `src/i18n/utils.ts` para adicionar a lógica de tradução de roteamento.
  ```js
  // src/i18n/utils.ts
  import { ui, linguaPadrao, mostrarLinguaPadrao, rotas } from './ui';

  export function usarCaminhoTraduzido(lang: keyof typeof ui) {
    return function traduzirCaminho(caminho: string, l: string = lang) {
      const nomeCaminho = caminho.replaceAll('/', '')
      const temTraducao = linguaPadrao !== l && rotas[l] !== undefined && rotas[l][nomeCaminho] !== undefined
      const caminhoTraduzido = temTraducao ? '/' + rotas[l][nomeCaminho] : path

      return !mostrarCaminhoTraduzido && l === linguaPadrao ? caminhoTraduzido : `/${l}${caminhoTraduzido}`
    }
  }
  ```

3. Crie uma função auxiliar para obter a rota, se ela existir baseada na URL atual, em `src/i18n/utils.ts`:
  ```js
  // src/i18n/utils.ts
  import { ui, linguaPadrao, mostrarLinguaPadrao, rotas } from './ui';

  export function pegarRotaDeUrl(url: URL): string | undefined {
    const nomeCaminho = new URL(url).pathname
    const partes = nomeCaminho?.split('/')
    const caminho = partes.pop() || partes.pop()

    if (caminho === undefined) {
      return undefined
    }

    const linguaAtual = pegarLangDeURL(url);

    if (linguaPadrao === linguaAtual) {
      const rota = Object.values(rotas)[0];
      return rota[caminho] !== undefined ? rota[caminho] : undefined
    }

    const pegarChavePeloValor = (obj: Record<string, string>, valor: string): string | undefined  => {
        return Object.keys(obj).find((chave) => obj[chave] === valor)
    }

    const chaveRevertida = pegarChavePeloValor(rotas[linguaAtual], caminho)

    if (chaveRevertida !== undefined) {
      return chaveRevertida
    }

    return undefined
  }
  ```

4. A função auxiliar pode ser usada para obter uma rota traduzida. Por exemplo, quando nenhuma rota traduzida é definida, o usuário será redirecionado para sua página inicial:
 ```astro
    ---
    // src/components/SeletorLinguas.astro
    import { languages } from '../i18n/ui';
    const rota = pegarRotaDeUrl(Astro.url);
    ---

    <ul>
      {Object.entries(linguas).map(([lang, label]) => (
        <li>
          <a href={traduzirCaminho(`/${rota ? rota : ''}`, lang)}>{label}</a>
        </li>
      ))}
    </ul>
    ```

## Recursos
- [Choosing a Language Tag](https://www.w3.org/International/questions/qa-choosing-language-tags)
- [Right-to-left Styling 101](https://rtlstyling.com/)

## Bibliotecas da comunidade
- [astro-i18next](https://github.com/yassinedoghri/astro-i18next) — Uma integração Astro para i18next incluindo alguns componentes utilitários.
- [astro-i18n](https://github.com/alexandre-fernandez/astro-i18n) — Uma biblioteca de internacionalização focada em TypeScript para Astro.
- [astro-i18n-aut](https://github.com/jlarmstrongiv/astro-i18n-aut) — Uma integração Astro para i18n que suporta `defaultLocale` sem geração de páginas. A integração é agnóstica a adaptadores e frameworks de UI.
